#include "StdAfx.h"
#include "RenderMesh.h"
#include "IIndexedMesh.h"
#include "SpuUtils.h"

static void transfer_writecombined(void* pDst, const void* pSrc, size_t size) SPU_NO_INLINE;
static void transfer_writecombined(void* pDst, const void* pSrc, size_t size) 
{

























	cryMemcpy(pDst, pSrc, size, MC_CPU_TO_GPU);

}

template<EStreamIDs stream, size_t Size> 
struct StreamCompactor; 

template<EStreamIDs stream, size_t Size> 
uint32 CompactStream(uint8 (&buffer)[Size], SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
{
	return StreamCompactor<stream, Size>::Compact(buffer, data, mesh, beg, end); 
}

template<size_t Size> 
struct StreamCompactor<VSF_GENERAL,Size>
{
	template<class T>
	struct cvt_t
	{
		const T p;

		cvt_t(const T pos): p(pos) {}

		operator T() const { return p; }

		operator Vec3() const
		{
			Vec3 v;

			v.x = CryConvertHalfToFloat(p.x);
			v.y = CryConvertHalfToFloat(p.y);
			v.z = CryConvertHalfToFloat(p.z);
			return v;
		}
	};


	template<class T>
	static cvt_t<T> pos2buf(const T& t)
	{
		cvt_t<T> cvt(t);
		return cvt;
	}

	template<typename T>
	static void CompactPositions(T* pVBuff, SSetMeshIntData& data, CMesh& mesh, const Vec3 &posOffset, uint32 beg, uint32 end)
	{
		if (mesh.m_pPositionsF16)	for (size_t i=0; i<end; ++i)	
			pVBuff[i].xyz = pos2buf(mesh.m_pPositionsF16[beg+i]);
		else for (size_t i=0; i<end; ++i)	
			pVBuff[i].xyz = mesh.m_pPositions[beg+i] - posOffset;
	}

	template<typename T>
	static void CompactPositions2(T* pVBuff, SSetMeshIntData& data, CMesh& mesh, const Vec3 &posOffset, uint32 beg, uint32 end)
	{
		if (mesh.m_pPositionsF16)	for (size_t i=0; i<end; ++i)
		{
			const CryHalf4& pos = mesh.m_pPositionsF16[beg+i];

			pVBuff[i].xy = *(CryHalf2*)&pos;

			pVBuff[i].z  = CryConvertHalfToFloat(pos.z);
		}
		else for (size_t i=0; i<end; ++i)
		{
			const Vec3 pos = mesh.m_pPositions[beg+i] - posOffset;
			pVBuff[i].xy = CryHalf2(pos.x, pos.y);
			pVBuff[i].z  = pos.z;
		}
	}

	static void CompactNormals(SVF_P3S_N4B_C4B_T2S* pVBuff, SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
	{
		if (mesh.m_pNorms) for (size_t i=0; i<end; ++i)
		{
			pVBuff[i].normal.bcolor[0] = (byte)(mesh.m_pNorms[beg+i][0] * 127.5f + 128.0f);
			pVBuff[i].normal.bcolor[1] = (byte)(mesh.m_pNorms[beg+i][1] * 127.5f + 128.0f);
			pVBuff[i].normal.bcolor[2] = (byte)(mesh.m_pNorms[beg+i][2] * 127.5f + 128.0f);
			SwapEndian(pVBuff[i].normal.dcolor);
		}
	}

	static void CompactNormals2(SVF_P2S_N4B_C4B_T1F* pVBuff, SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
	{
		if (mesh.m_pNorms) for (size_t i=0; i<end; ++i)
		{
			pVBuff[i].normal.bcolor[0] = (byte)(mesh.m_pNorms[beg+i][0] * 127.5f + 128.0f);
			pVBuff[i].normal.bcolor[1] = (byte)(mesh.m_pNorms[beg+i][1] * 127.5f + 128.0f);
			pVBuff[i].normal.bcolor[2] = (byte)(mesh.m_pNorms[beg+i][2] * 127.5f + 128.0f);
			SwapEndian(pVBuff[i].normal.dcolor);
		}
	}

	template<typename T>
	static void CompactColors0(T* pVBuff, SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
	{
		if (mesh.m_pColor0) for (size_t i=0; i<end; ++i)
		{
			pVBuff[i].color.bcolor[0] = mesh.m_pColor0[beg+i].b;
			pVBuff[i].color.bcolor[1] = mesh.m_pColor0[beg+i].g;
			pVBuff[i].color.bcolor[2] = mesh.m_pColor0[beg+i].r;
			pVBuff[i].color.bcolor[3] = mesh.m_pColor0[beg+i].a;
			SwapEndian(pVBuff[i].color.dcolor);
		}
		else for (size_t i=0; i<end; ++i)
			pVBuff[i].color.dcolor = ~0;
	}

	static void CompactColors1(SVF_P2S_N4B_C4B_T1F* pVBuff, SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
	{
		if (mesh.m_pColor1) for (size_t i=0; i<end; ++i)
		{
#     ifndef XENON
			pVBuff[i].normal.bcolor[3] = mesh.m_pColor1[beg+i].b;


#     endif
		}
	}

	template<typename T> 
	static void CompactUVs(T* pVBuff, SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
	{
		if (mesh.m_pTexCoord) for (size_t i=0; i<end; ++i)
			pVBuff[i].st = Vec2f16(mesh.m_pTexCoord[beg+i].s, mesh.m_pTexCoord[beg+i].t);
		// Value less than -1 tells vertex program to use usual (old) terrain tex gen (temporary)
		// Real coordinates from vertex are used only in case of 3d terrain research (CVoxTerrain)
		else for (uint32 i=0; i<end; ++i)
			pVBuff[i].st =  Vec2f16(-100.f, -100.f);
	}

	static uint32 Compact(uint8 (&buffer)[Size], SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
	{
		Vec3 posOffset = data.m_pPosOffset ? *data.m_pPosOffset : Vec3(0,0,0);

		if (data.m_pVBuff) 
		{
			uint32 dstPad = 0; 




		
			SVF_P3S_C4B_T2S *pVBuff = alias_cast<SVF_P3S_C4B_T2S *>(&buffer[dstPad]);
			uint32 amount = min((end-beg),(uint32)(Size/sizeof(pVBuff[0])));

			CompactPositions(pVBuff, data, mesh, posOffset, beg, amount);
			CompactUVs(pVBuff, data, mesh, beg, amount);
			CompactColors0(pVBuff, data, mesh, beg, amount);

			transfer_writecombined(&data.m_pVBuff[beg], &buffer[dstPad], amount*sizeof(pVBuff[0]));

			return amount; 
		}
		else if (data.m_pVBuffV)
		{
			uint32 dstPad = 0; 




		
			SVF_P2S_N4B_C4B_T1F *pVBuff = alias_cast<SVF_P2S_N4B_C4B_T1F*>(&buffer[dstPad]);
			uint32 amount = min((end-beg),(uint32)(Size/sizeof(pVBuff[0])));

			CompactPositions2(pVBuff, data, mesh, posOffset, beg, amount);
			CompactNormals2(pVBuff, data, mesh, beg, amount);
			CompactColors0(pVBuff, data, mesh, beg, amount);
			CompactColors1(pVBuff, data, mesh, beg, amount);

			transfer_writecombined(&data.m_pVBuffV[beg], &buffer[dstPad], amount*sizeof(pVBuff[0]));

			return amount; 
		}

		CryFatalError("CRenderMesh::SetMesh_Impl: invalid vertex format for general stream");
		return 0; 
	}
}; 

template<size_t Size> 
struct StreamCompactor<VSF_TANGENTS,Size>
{
	static uint32 Compact(uint8 (&buffer)[Size], SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
	{
		if (mesh.m_pTangents == NULL || data.m_pTBuff == NULL)
			return end; 

		uint32 dstPad = 0; 




		
		SPipTangents *pTBuff = alias_cast<SPipTangents *>(&buffer[dstPad]);
		uint32 amount = min((end-beg),(uint32)(Size/sizeof(pTBuff[0])));

		for (size_t i=0; i<amount; ++i)
		{
			pTBuff[i].Binormal = mesh.m_pTangents[beg+i].Binormal;
			pTBuff[i].Tangent = mesh.m_pTangents[beg+i].Tangent;
		}

		transfer_writecombined(&data.m_pTBuff[beg], &buffer[dstPad], amount*sizeof(data.m_pTBuff[0]));

		return amount; 
	}
}; 

template<size_t Size> 
struct StreamCompactor<VSF_QTANGENTS,Size>
{
	static uint32 Compact(uint8 (&buffer)[Size], SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
	{
		if (mesh.m_pQTangents == NULL || data.m_pQTBuff == NULL)
			return end; 

		uint32 dstPad = 0; 




		
		SQTangents *pQTBuff = alias_cast<SQTangents *>(&buffer[dstPad]);
		uint32 amount = min((end-beg),(uint32)(Size/sizeof(pQTBuff[0])));

		for (size_t i=0; i<amount; ++i)
			pQTBuff[i].Tangent = mesh.m_pQTangents[beg+i].TangentBinormal;

		transfer_writecombined(&data.m_pQTBuff[beg], &buffer[dstPad], amount*sizeof(pQTBuff[0]));

		return amount; 
	}
}; 

#if ENABLE_NORMALSTREAM_SUPPORT
template<size_t Size> 
struct StreamCompactor<VSF_NORMALS,Size>
{
	static uint32 Compact(uint8 (&buffer)[Size], SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
	{
		if (mesh.m_pNorms == NULL || data.m_pNormalsBuff == NULL)
			return end; 

		uint32 dstPad = 0; 





		uint32 amount = min((end-beg),(uint32)(Size/sizeof(data.m_pNormalsBuff[0])));
		memcpy(&buffer[dstPad], &mesh.m_pNorms[beg], amount*sizeof(data.m_pNormalsBuff[0]));
		transfer_writecombined(&data.m_pNormalsBuff[beg], &buffer[dstPad], amount*sizeof(data.m_pNormalsBuff[0]));

		return amount; 
	}
}; 
#endif

template<size_t Size> 
struct StreamCompactor<VSF_HWSKIN_MORPHTARGET_INFO,Size>
{
	static uint32 Compact(uint8 (&buffer)[Size], SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
	{
		if (mesh.m_pShapeDeformation == NULL || data.m_pMorphTargets == NULL)
			return end; 

		uint32 dstPad = 0; 





		uint32 amount = min((end-beg),(uint32)(Size/sizeof(data.m_pMorphTargets[0])));
		memset(&buffer[dstPad], 0x0, amount*sizeof(data.m_pMorphTargets[0]));
		transfer_writecombined(&data.m_pMorphTargets[beg], &buffer[dstPad], amount*sizeof(data.m_pMorphTargets[0]));

		return amount; 
	}
}; 

#if ENABLE_SHAPEDEFORMATION_SUPPORT
template<size_t Size> 
struct StreamCompactor<VSF_HWSKIN_SHAPEDEFORM_INFO,Size>
{
	static uint32 Compact(uint8 (&buffer)[Size], SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
	{
		if (mesh.m_pShapeDeformation == NULL || data.m_pShapeDeformBuff == NULL)
			return end; 

		uint32 dstPad = 0; 





		SVF_P3F_P3F_I4B *pShapeDeformBuff = alias_cast<SVF_P3F_P3F_I4B *>(&buffer[dstPad]);
		uint32 amount = min((end-beg),(uint32)(Size/sizeof(pShapeDeformBuff[0])));

		for (uint32 i=0; i<amount; i++)
		{
			pShapeDeformBuff[i].thin            = mesh.m_pShapeDeformation[beg+i].thin;
			pShapeDeformBuff[i].fat             = mesh.m_pShapeDeformation[beg+i].fat;

			// How does this actually work below? Where is pShapeDeformBuff[i].index actually set?
			pShapeDeformBuff[i].index.bcolor[0] = (pShapeDeformBuff[i].index.bcolor[0] > 0x80) ? 0xff : 0x00;
			pShapeDeformBuff[i].index.bcolor[1] = (pShapeDeformBuff[i].index.bcolor[1] > 0x80) ? 0xff : 0x00;
			pShapeDeformBuff[i].index.bcolor[2] = (pShapeDeformBuff[i].index.bcolor[2] > 0x80) ? 0xff : 0x00;
			pShapeDeformBuff[i].index.bcolor[3] = 0;

			//calcultate the index (this is the only value we need in the vertex-shader)
			if (pShapeDeformBuff[i].index.bcolor[0]) pShapeDeformBuff[i].index.bcolor[3] |= 1;
			if (pShapeDeformBuff[i].index.bcolor[1]) pShapeDeformBuff[i].index.bcolor[3] |= 2;
			if (pShapeDeformBuff[i].index.bcolor[2]) pShapeDeformBuff[i].index.bcolor[3] |= 4;

			SwapEndian(pShapeDeformBuff[i].index.dcolor);
		}

		transfer_writecombined(&data.m_pShapeDeformBuff[beg], &buffer[dstPad], amount*sizeof(data.m_pShapeDeformBuff[0]));
		return amount; 
	}
}; 
#endif


template<size_t Size> 
uint32 CompactIndices(uint8 (&buffer)[Size], SSetMeshIntData& data, CMesh& mesh, uint32 beg, uint32 end)
{
	if (mesh.m_pIndices == NULL || data.m_pInds == NULL) 
		return end; 

	uint32 dstPad = 0;





	uint32 amount = min((end-beg),(uint32)(Size/sizeof(mesh.m_pIndices[0])));
	memcpy(&buffer[dstPad], &mesh.m_pIndices[beg], amount*sizeof(mesh.m_pIndices[0]));
	transfer_writecombined(&data.m_pInds[beg], &buffer[dstPad], amount*sizeof(data.m_pInds[0]));
	return amount; 
}

#if !defined (__SPU__)
	typedef _MS_ALIGN(128) uint8 AlignedStagingBufferT[(8<<10)+128];
#endif 

SPU_ENTRY(RenderMesh_SetMesh)
void CRenderMesh::SetMesh_IntImpl( SSetMeshIntData data )																	 																	 
{	
	SpuStackValue<CMesh,false,true> stackMesh(*data.m_pMesh);
	CMesh &mesh = stackMesh;



	AlignedStagingBufferT stagingBuffer;


	//////////////////////////////////////////////////////////////////////////
	// Compact the seperate streams from the CMesh instance into a general 
	//////////////////////////////////////////////////////////////////////////
	for (uint32 iter=0; iter<data.m_nVerts; iter+=CompactStream<VSF_GENERAL>(stagingBuffer, data, mesh, iter, data.m_nVerts));
	for (uint32 iter=0; iter<data.m_nVerts; iter+=CompactStream<VSF_TANGENTS>(stagingBuffer, data, mesh, iter, data.m_nVerts));
	for (uint32 iter=0; iter<data.m_nVerts; iter+=CompactStream<VSF_QTANGENTS>(stagingBuffer, data, mesh, iter, data.m_nVerts));
# if ENABLE_NORMALSTREAM_SUPPORT
	for (uint32 iter=0; iter<data.m_nVerts; iter+=CompactStream<VSF_NORMALS>(stagingBuffer, data, mesh, iter, data.m_nVerts));
# endif
	for (uint32 iter=0; iter<data.m_nVerts; iter+=CompactStream<VSF_HWSKIN_MORPHTARGET_INFO>(stagingBuffer, data, mesh, iter, data.m_nVerts));
# if ENABLE_SHAPEDEFORMATION_SUPPORT
	for (uint32 iter=0; iter<data.m_nVerts; iter+=CompactStream<VSF_HWSKIN_SHAPEDEFORM_INFO>(stagingBuffer, data, mesh, iter, data.m_nVerts));
# endif
	for (uint32 iter=0; iter<data.m_nInds; iter+=CompactIndices(stagingBuffer, data, mesh, iter, data.m_nInds));

	// Make sure the last transfer buffer has finished copying before we exit the spu 



}
